
/*
*********************************
	  SF2D Engine
*********************************

	By Julian Williams
	September 29, 2010
**********************************

	Description:

		Tower type Entity 
***********************************
*/

#include <iostream>
using namespace std;

#include "app.h" //need reference to render window, will get rid of this dependency later.
#include "physics_world.h"
#include "script_engine.h"
#include "image_manager.h"
#include "cfg_reader.h"
#include "helper.h"
#include "entity_factory.h"

#include "tower.h"



list< Entity* > *Tower::entity_list_reference;

//Put this in helper.h for ease of use
float distanceFormula(sf::Vector2f point_1, sf::Vector2f point_2)
{
	b2Vec2 dist_vector;
	dist_vector.x = (point_2.x - point_1.x);
	dist_vector.y = (point_2.y - point_1.y);


	float distance = sqrtFast( (dist_vector.x * dist_vector.x) + (dist_vector.y * dist_vector.y) );

	return distance;
}



//a private copy function so that i don't have to
//copy/paste the copy constructor to the
//equal operator
void Tower::copyEntity( const Entity *e)
{
	
	Tower *e_copy = (Tower*)e;
	Entity::copyEntity(e);

	range = e_copy->GetRange();
	rate_of_fire = e_copy->rate_of_fire;

	projectile_name = e_copy->projectile_name;

	base_img = e_copy->base_img;
	gun_img = e_copy->gun_img;

	baseSprite.SetOrigin(e_copy->baseSprite.GetOrigin());
	gunSprite.SetOrigin(e_copy->gunSprite.GetOrigin());

	

	//baseSprite = e.baseSprite;



	
//Path finding stuff
	//pathComponent = e.pathComponent;
//	special = 23;
}//EoF


Tower::Tower()
{
	refCount = 1;
	cout << "Called lol?" << endl;
}//EoF


//Copy Constructor
Tower::Tower(const Entity &other)
{
	target = new Entity();
	has_target = false;
	fire_timer = rate_of_fire;


	refCount = 1;
	copyEntity(&other);
	//PrepareSpawn(Map.pathStart);
}//EoF



/************************
  AngelScript functions
*************************/

void Tower_SetGunAngle(Tower *e, float angle)
{
	e->gunSprite.SetRotation(angle);
}//eoF

void Tower_RotateGun(Tower *e, float angle)
{
	e->gunSprite.Rotate(angle);
}//eoF



//Register sheeit
bool Tower::Register_With_ScriptEngine()
{
	int r = -1;
	//Register Properties
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void SetGunAngle(float)", asFUNCTION( Tower_SetGunAngle), asCALL_CDECL_OBJFIRST);				assert( r >= 0 );
		r = aScriptEngine.GetEngine()->RegisterObjectMethod("Entity", "void RotateGun(float)", asFUNCTION( Tower_RotateGun), asCALL_CDECL_OBJFIRST);		assert( r >= 0 );

		r = aScriptEngine.GetEngine()->RegisterObjectProperty("Entity", "Entity target",		offsetof(Tower, target));	;		assert( r >= 0 );

		//Error check
		if( r < 0) 
		{ 
			cout << "[TOWER] - failed to register script interface. \n";
			return false; 
		}


		return true;
}//EoF



//Prepare Spawn
void Tower::PrepareSpawn(const b2Vec2 &start)
{
	Entity::PrepareSpawn(start);

	//Physics things
	bodyDef.position = METERS_PER_PIXEL * start;
	body = physicsManager.world->CreateBody( &bodyDef );
	fixtureDef.shape = &shapeDef;
	body->CreateFixture( &fixtureDef  );

	body->SetUserData((void*)(this));

	//Setup Images
	baseSprite.SetImage( *base_img );
	gunSprite.SetImage( *gun_img );
	

}//EoF







/**************************************************
************** TARGET LEADING CODE ****************
**************************************************/



b2Vec2 get_dir_towards(const b2Vec2& source, const b2Vec2& target)
{
    const float cos_a = (target.y - source.y) / sqrtFast(pow(target.x - source.x, 2) + pow(target.y - source.y, 2));
    const float sin_a = (target.x - source.x) / sqrtFast(pow(target.x - source.x, 2) + pow(target.y - source.y, 2));

    return b2Vec2(sin_a, cos_a);
}




float largest_root_of_quadratic_equation(float a, float b, float c)
{
    return (-b + sqrt(b * b - 4 * a * c)) / (2 * a);
}



/* Primary Aiming function */
float AimAhead(b2Vec2 delta, b2Vec2 vr, float muzzleV)
{
  // Quadratic equation coefficients a*t^2 + b*t + c = 0
  float a = b2Dot(vr, vr) - muzzleV*muzzleV;
  float b = 2*b2Dot(vr, delta);
  float c = b2Dot(delta, delta);
  if(c == 0)
  {
	  cout << "Uh Oh, Quadratic fail\n";
  }
  
  float det = b*b - 4*a*c;

  //cout << det << endl;
  // If the determinant is negative, then there is no solution
  if(det > 0)
  {
    return 2*c/(sqrtFast(det) - b);
 } else {
    return -1;
  }

}


/* Secondary Aiming Function 

float AimAhead(b2Vec2 delta, b2Vec2 vr, float muzzleV)
{
  // Quadratic equation coefficients a*t^2 + b*t + c = 0
  float a = b2Dot(vr, vr) - muzzleV*muzzleV;
  float b = -2*b2Dot(vr, delta);
  float c = -b2Dot(delta, delta);

  return largest_root_of_quadratic_equation(a, b, c);


}
*/


/*	
    float a = (bullet_speed * bullet_speed) - b2Dot(target_velocity, target_velocity);
    float b = -2 * b2Dot(target_velocity, (target - shooter));
    float c = -b2Dot(target - shooter, target - shooter);

    return target +  (largest_root_of_quadratic_equation(a, b, c) * target_velocity) ;
	
}

*/





bool Aim_Turret_At(Tower& tower)
{
	
    b2Vec2 turret_pos(tower.gunSprite.GetPosition().x, tower.gunSprite.GetPosition().y);
    b2Vec2 target_pos(tower.GetTarget()->GetPosition().x, tower.GetTarget()->GetPosition().y);

	b2Vec2 delta = target_pos - turret_pos;


	b2Vec2 facing_velocity;
	float temp_rot = -1*atan2(delta.y, delta.x);
	facing_velocity.x =  cos(  temp_rot ) * tower.speed;
	facing_velocity.y = -sin(  temp_rot ) * tower.speed;

    b2Vec2 target_velocity = tower.GetTarget()->body->GetLinearVelocity();


	// Find the relative velocities
	b2Vec2 vr = target_velocity - facing_velocity;

	float t =  AimAhead(delta, vr, tower.speed) ;

	b2Vec2 aimPoint;
	if(t > 0)
	{
  // Aim at the point where the target will be at the time
  // of the collision.

		aimPoint = delta + 3.7*t*target_velocity;
	}else {
		aimPoint = delta + t*target_velocity;
	}

		float tower_angle_radian = -1*atan2(aimPoint.y, aimPoint.x);
		float tower_angle_degrees = radiansToDegrees( tower_angle_radian );
		tower.gunSprite.SetRotation( tower_angle_degrees  ) ;
		
	
	return true;
	//return false;
   
}

/***********************************************/





void Tower::Update_Logic(asIScriptContext *context)
{
	Entity::Update_Logic(context);

	//Update Graphics to Physics' Engine's Positions
	sf::Vector2f base_positions, gun_positions, real_positions;

	real_positions.x = (body->GetPosition().x* PIXELS_PER_METER);
	real_positions.y = (body->GetPosition().y* PIXELS_PER_METER);

	
	base_positions = real_positions;
	gun_positions = real_positions;


	baseSprite.SetPosition( base_positions );
	gunSprite.SetPosition( gun_positions );





//Temporary stuff, all this goes to scripts


	if((!has_target) ||
	   (distanceFormula(gunSprite.GetPosition(), target->GetPosition()) > range))
	{
		//Calls the find target function every frame until new target is found
		//Very inefficient, i can optimize this to only call this every half 
		//second or something like that, if i run into performance issues, 
		//I know what to work on first!
		has_target = FindTarget();


		gunSprite.Rotate(2);


	}else{
		//sf::Vector2f oldTarget_pos = target->GetPosition();
		//sf::Vector2f newTarget_pos = App::getInstance().GetGameViewCoords( oldTarget_pos );


	//Handle tower rotations
		/*
		float tower_angle_radian = -1*atan2( gunSprite.GetPosition().y - target->GetPosition().y, gunSprite.GetPosition().x - target->GetPosition().x);
		float tower_angle_degrees = radiansToDegrees( tower_angle_radian );
		gunSprite.SetRotation( tower_angle_degrees + 180 );
		*/

		if( Aim_Turret_At(*this) )
		{



	//Handle tower firing
			if(--fire_timer <= 0)
			{
				fire_timer = rate_of_fire;
			
				EntityEmmiter emit;
				(float)emit.start_pos.x = (gunSprite.GetPosition().x + cos(-1 *degreesToRadians(gunSprite.GetRotation()))*60);
				(float)emit.start_pos.y = (gunSprite.GetPosition().y + (-sin(degreesToRadians(gunSprite.GetRotation())))*60);
	
				(float)emit.velocity.x = cos(degreesToRadians(gunSprite.GetRotation()))*speed;
				(float)emit.velocity.y = -sin(degreesToRadians(gunSprite.GetRotation()))*speed;

				emit.name = projectile_name;
				emit.rotation = gunSprite.GetRotation();

			//	cout << gunSprite.GetPosition().x << ", " << gunSprite.GetPosition().y << endl;;
				entityManager.Spawn(emit);
			
			}
		//cout << fire_timer << endl;

		//if target has left the playing field
			if(target->Get_State() == ES_NONE)
			{
				has_target = false;
			}
		}else {
		//	has_target = false;
		}//End ov aiming
	}//end ov has target

	//gunSprite.Rotate(5);
}//EoF




void Tower::Render(sf::RenderWindow * rendow)
{		
	//Entity::Render(rendow);
	rendow->Draw(baseSprite);
	rendow->Draw(gunSprite);
}//EoF

/* Get the Ammo Configuration file and load properties */
bool Tower::Get_Config(const string& resource_dir)
{

	
	//grab and create the nessisary directories
	string base_img_location = resource_dir + string("base.png");
	string gun_img_location = resource_dir + string("gun.png");
	string cfg_location = resource_dir + string("properties.cfg");
	//string script_name = App::getInstance().Resources_Dir["scripts"];
	//string snd_location = folder_location + string("fire.wav");

	cout << "Loading Entity folder: " << resource_dir << endl;
	cout << "Config from: " << cfg_location << endl;
	cout << "Base Image from: " << base_img_location << endl;
	cout << "gun Image from: " << gun_img_location << endl;

	//cout << "Gun sound from: " << snd_location << endl;



//Now Grab the firing sound for the gun
	//if(!Load_Sounds(s_fire, SND_fire, snd_location)) { return false; }




//Grab particle Config
		cfg_reader cfg(cfg_location, true);


//Get all of the properties
			type = cfg.Get_Info("info:type", 1);
			name = cfg.Get_Info("info:name", "Default");
			projectile_name = cfg.Get_Info("info:projectile_name", "lol");

			damage = cfg.Get_Info("info:damage", 10);
			speed = cfg.Get_Info("info:speed", 100);

			range = cfg.Get_Info("info:range", 300);
			rate_of_fire = cfg.Get_Info("info:rate_of_fire", 100);
		//	script_name += cfg.Get_Info("info:script_driver", "NULL");


//Animation related information
			tile_size.x = cfg.Get_Info("animation:tile_width", 32);
			tile_size.y = cfg.Get_Info("animation:tile_height", 32);


		//Get/set base' origin
			sf::Vector2f origin;
			origin.x = cfg.Get_Info("animation:origin_x",16);
			origin.y = cfg.Get_Info("animation:origin_y",16);
			baseSprite.SetOrigin( origin );

		//Get/set gun origin	
			gunSprite.SetOrigin(20, 20);

			//cout << "Particle Image from: " << image_location << "\n\n";

		//Load base Image used for each type
			base_img = gImageManager.getResource( base_img_location );
			if(base_img == NULL) { return false; }
			baseSprite.SetImage( *base_img );

			gun_img = gImageManager.getResource( gun_img_location );
			if(gun_img == NULL) { return false; }
			gunSprite.SetImage( *gun_img );

//Setup Physics

			//bodyDef.position.Set(width/2*METERS_PER_PIXEL, height/2*METERS_PER_PIXEL);
//create the dynamic body
			if(speed > 15000)
			{
				bodyDef.bullet = true;
			}

			bodyDef.type = static_cast<b2BodyType> (cfg.Get_Info("physics:body_type", b2_dynamicBody));
			bodyDef.linearDamping = cfg.Get_Info("physics:linear_damping", 2);
			bodyDef.fixedRotation = cfg.Get_Info("physics:fixed_rotation", false);

			//shape of the dynamic body
			shapeDef.SetAsBox(tile_size.x/2*METERS_PER_PIXEL, tile_size.y/2*METERS_PER_PIXEL);
		
			//circle.m_p.Set(50*MPP, 50*MPP);
			circleDef.m_radius = (tile_size.x/2*METERS_PER_PIXEL);
		
			fixtureDef.shape		= &shapeDef;

			fixtureDef.filter.categoryBits = 0x0002;
			fixtureDef.filter.maskBits = 0x0004;

			fixtureDef.density		= cfg.Get_Info("physics:density", 1.0);
			fixtureDef.friction		= cfg.Get_Info("physics:friction", 0.3); 
			fixtureDef.restitution	= cfg.Get_Info("physics:bounciness", 0.5);
			fixtureDef.isSensor		= cfg.Get_Info("physics:is_sensor", false);

			//apply the fixture to the body
			//body->CreateFixture(&fixtureDef);

	return true;
}//EoF






//Find target to shoot at
bool Tower::FindTarget()
{

	//loop through all entities
	list< Entity *>::iterator i;

	for(i = entity_list_reference->begin(); i != entity_list_reference->end(); i++)
	{
		switch((*i)->type)
		{
		//if type is creep, then we loop through and check to see if its within range,
		//if so, make it the target and exit loop, I'll come up with different targeting
		//algorithm's like lowest/heighest health, or closest to goal, later!
		case ET_CREEP:
			if(distanceFormula( gunSprite.GetPosition(), (*i)->GetPosition() ) <= range)
			{
				target = (*i);
				return true;
				//goto exit_for; //Not good enough damn it, not good enough!
			}
			break;

		//if type is not at creeps, then skip it
		case ET_CREEP+1:
			return false;
			break;

		//if we've looped through all the creeps, then exit loop
		default:
		//	goto exit_for;
			continue;
			break;
		}//end ov switch	
	}//End ov for

//Super bad coding practice right here, srsly Julian? fix it.
//exit_for: ;
		return false;	
}//EoF